﻿// FILE           :Puppeteer.cs
// PROJECT        :SETCritters                                        
// PROGRAMMER     :Kenneth Jinks and Ryan Pink                                         
// FIRST VERSION  :November 26 2013                                                
// DESCRIPTION    :
//      This file contains the class definition for the Puppeteer object. This object uses
//      a bundle of critter data in the form of a CritterType struct, and animates it based on
//      a collection of preset motions and patterns.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Orbs2Revengeance;
using System.Drawing;
using SimplexNoise;

namespace ThePuppeteer
{
    class Puppeteer
    {
        private const int JUMP_SPEED = 2;
        private const int WALK_SPEED = 80;
        private const bool MIRRORED = false;

        //a dictionary of predefined motions
        Dictionary<string, CritterMotionType> motion = new Dictionary<string,CritterMotionType>();
        static Random rand  = new Random();
        Point puppetPosition = new Point(rand.Next(800), rand.Next(800));
        int jumpHeight = 10;
        Point eyeTarget = new Point(0, 0);

        #region ACCESSORS & MUTATORS
        public Point EyeTarget
        {
            get
            {
                return eyeTarget;
            }
            set
            {
                eyeTarget = value;
            }
        }

        public Point PuppetPosition
        {
            get {
                return puppetPosition;
            }
            set {
                puppetPosition = value;
            }
        }
        #endregion


        public Puppeteer()
        {
            Initialize();
        }


        /// <summary>
        ///     This function populates dictionary of motions with a collection of presets.
        /// </summary>
        void Initialize()
        {
            //populate motion dictionary
            
            CritterMotionType tempMotion = new CritterMotionType();

            #region MotionParams
            tempMotion.headExtendRange     =  0.0f;
            tempMotion.headExtendRate      =  0.0f;
            tempMotion.headSwingRange      =  0.0f;
            tempMotion.headSwingRate       =  0.0f;
            
            tempMotion.leftArmExtendRange  =  0.0f;
            tempMotion.leftArmExtendRate   =  0.0f;
            tempMotion.leftArmSwingRange   =  0.0f;
            tempMotion.leftArmSwingRate    =  0.0f;
            
            tempMotion.rightArmExtendRange =  0.0f;
            tempMotion.rightArmExtendRate  =  0.0f;
            tempMotion.rightArmSwingRange  =  0.0f;
            tempMotion.rightArmSwingRate   =  0.0f;
            
            tempMotion.leftLegExtendRange  =  0.0f;
            tempMotion.leftLegExtendRate   =  0.0f;
            tempMotion.leftLegSwingRange   =  0.0f;
            tempMotion.leftLegSwingRate    =  0.0f;
            
            tempMotion.rightLegExtendRange =  0.0f;
            tempMotion.rightLegExtendRate  =  0.0f;
            tempMotion.rightLegSwingRange  =  0.0f;
            tempMotion.rightLegSwingRate   =  0.0f;
            #endregion

            motion.Add("still", tempMotion);

            #region MotionParams
            tempMotion.headExtendRange     =  0.0f;
            tempMotion.headExtendRate      =  0.0f;
            tempMotion.headSwingRange      =  0.3f;
            tempMotion.headSwingRate       =  0.1f;
            
            tempMotion.leftArmExtendRange  =  0.0f;
            tempMotion.leftArmExtendRate   =  0.0f;
            tempMotion.leftArmSwingRange   =  0.3f;
            tempMotion.leftArmSwingRate    =  0.01f;
            
            tempMotion.rightArmExtendRange =  0.0f;
            tempMotion.rightArmExtendRate  =  0.0f;
            tempMotion.rightArmSwingRange  =  0.3f;
            tempMotion.rightArmSwingRate   =  0.01f;
            
            tempMotion.leftLegExtendRange  =  5.0f;
            tempMotion.leftLegExtendRate   =  0.1f;
            tempMotion.leftLegSwingRange   =  0.0f;
            tempMotion.leftLegSwingRate    =  0.0f;
            
            tempMotion.rightLegExtendRange =  5.0f;
            tempMotion.rightLegExtendRate  =  0.1f;
            tempMotion.rightLegSwingRange  =  0.0f;
            tempMotion.rightLegSwingRate   =  0.0f;
            #endregion

            motion.Add("standing", tempMotion);

            #region MotionParams
            tempMotion.headExtendRange     =  0.0f;
            tempMotion.headExtendRate      =  0.0f;
            tempMotion.headSwingRange      =  0.3f;
            tempMotion.headSwingRate       =  0.1f;
            
            tempMotion.leftArmExtendRange  =  0.0f;
            tempMotion.leftArmExtendRate   =  0.0f;
            tempMotion.leftArmSwingRange   =  0.2f;
            tempMotion.leftArmSwingRate    =  0.5f;
            
            tempMotion.rightArmExtendRange =  0.0f;
            tempMotion.rightArmExtendRate  =  0.0f;
            tempMotion.rightArmSwingRange  =  0.3f;
            tempMotion.rightArmSwingRate   =  0.01f;
            
            tempMotion.leftLegExtendRange  =  5.0f;
            tempMotion.leftLegExtendRate   =  0.1f;
            tempMotion.leftLegSwingRange   =  0.0f;
            tempMotion.leftLegSwingRate    =  0.0f;
            
            tempMotion.rightLegExtendRange =  5.0f;
            tempMotion.rightLegExtendRate  =  0.1f;
            tempMotion.rightLegSwingRange  =  0.0f;
            tempMotion.rightLegSwingRate   =  0.0f;
            #endregion

            motion.Add("waving", tempMotion);

            #region MotionParams
            tempMotion.headExtendRange     =  0.0f;
            tempMotion.headExtendRate      =  0.0f;
            tempMotion.headSwingRange      =  0.3f;
            tempMotion.headSwingRate       =  0.1f;
            
            tempMotion.leftArmExtendRange  =  0.0f;
            tempMotion.leftArmExtendRate   =  0.0f;
            tempMotion.leftArmSwingRange   =  0.2f;
            tempMotion.leftArmSwingRate    =  0.5f;
            
            tempMotion.rightArmExtendRange =  0.0f;
            tempMotion.rightArmExtendRate  =  0.0f;
            tempMotion.rightArmSwingRange  =  0.3f;
            tempMotion.rightArmSwingRate   =  0.01f;
            
            tempMotion.leftLegExtendRange  =  5.0f;
            tempMotion.leftLegExtendRate   =  0.1f;
            tempMotion.leftLegSwingRange   =  0.4f;
            tempMotion.leftLegSwingRate    =  0.3f;
            
            tempMotion.rightLegExtendRange =  5.0f;
            tempMotion.rightLegExtendRate  =  0.1f;
            tempMotion.rightLegSwingRange  =  0.4f;
            tempMotion.rightLegSwingRate   =  0.3f;
            #endregion

            motion.Add("walking", tempMotion);

            #region MotionParams
            tempMotion.headExtendRange     =  0.0f;
            tempMotion.headExtendRate      =  0.0f;
            tempMotion.headSwingRange      =  0.0f;
            tempMotion.headSwingRate       =  0.0f;
            
            tempMotion.leftArmExtendRange  =  0.0f;
            tempMotion.leftArmExtendRate   =  0.0f;
            tempMotion.leftArmSwingRange   =  0.3f;
            tempMotion.leftArmSwingRate    =  0.8f;
            
            tempMotion.rightArmExtendRange =  0.0f;
            tempMotion.rightArmExtendRate  =  0.0f;
            tempMotion.rightArmSwingRange  =  0.3f;
            tempMotion.rightArmSwingRate   =  0.8f;
            
            tempMotion.leftLegExtendRange  = 20.0f;
            tempMotion.leftLegExtendRate   =  0.8f;
            tempMotion.leftLegSwingRange   =  0.0f;
            tempMotion.leftLegSwingRate    =  0.0f;
            
            tempMotion.rightLegExtendRange = 20.0f;
            tempMotion.rightLegExtendRate  =  0.8f;
            tempMotion.rightLegSwingRange  =  0.0f;
            tempMotion.rightLegSwingRate   =  0.0f;
            #endregion

            motion.Add("jumping", tempMotion);
        }

        
        /// <summary>
        ///     This method, when given critter data, a canvas and a frame, applies the appropriate
        ///     critter motion information and renders it to the provided bitmap.
        /// </summary>
        /// <param name="canvas">bitmap image to render the critter to</param>
        /// <param name="critter">critter data to be processed</param>
        /// <param name="frame">current critter frame of animation</param>
        public void GetFrame(Bitmap canvas, CritterType critter, int frame)
        {
            CritterNursery critterNursery = new CritterNursery();
            CritterType reflectionCritter = critter;

            if (eyeTarget != new Point(0, 0))
            {
                critterNursery.EyeTarget = eyeTarget;
            }
            else
            {
                critterNursery.EyeTarget = frmOrbs.MousePosition;
            }

            //simplex noise is used, this creates spatial noise with three different harmonics
            //to give a fluid and organic time distribution of blinks
            //this technique is used in more place in this method for the same reasons
            int timeToBlink = (int)(Noise.Generate((1234299.0f + frame) / 30, 100, 12) *
                                    Noise.Generate((55455459.0f + frame) / 75, 123, 12) *
                                    Noise.Generate((923499.0f + frame) / 10, 333, 56) * 10000.0f);

            if ( timeToBlink > -2500)
            {
                
                 critterNursery.Blink();
            }

            critter.bodyPosition = puppetPosition;

            reflectionCritter = critter;
            reflectionCritter.bodyPosition.X = canvas.Width - reflectionCritter.bodyPosition.X;
            CritterType tempCritter = critter;
            CritterType tempReflectionCritter = reflectionCritter;

            tempCritter.bodyPosition.Y -= jumpHeight;

            tempReflectionCritter.bodyPosition.Y -= jumpHeight;
            
            switch((int)Math.Abs(Math.Sin(Noise.Generate(frame / 180.0f, 44) *
                                 Noise.Generate(frame / 3600.0f, 11) *
                                 Noise.Generate(frame / 7200.0f, 22) *
                                 Noise.Generate(frame / 14400.0f, 33)) * 
                                 144.0f) * (int)(Math.PI / 2.0f) % 4)
            {
                case (3):

                    if (jumpHeight > 1)
                    {
                        jumpHeight -= JUMP_SPEED;
                    }

                    critterNursery.GetCritterFrame(canvas, 
                                                   tempCritter, 
                                                   motion["standing"], 
                                                   frame);
                    if (MIRRORED == true)
                    { 
                        critterNursery.GetCritterFrame(canvas,
                                                   tempReflectionCritter,
                                                   motion["standing"],
                                                   frame);
                    }
                    break;
                case (2):
                    if (jumpHeight > 1)
                    {
                        jumpHeight -= JUMP_SPEED;
                    }

                    critterNursery.GetCritterFrame(canvas,
                                                   tempCritter,
                                                   motion["waving"],
                                                   frame);
                    if (MIRRORED == true)
                    {
                        critterNursery.GetCritterFrame(canvas,
                                                       tempReflectionCritter,
                                                       motion["waving"],
                                                       frame);
                    }
                    break;
                case (0):
                    if (jumpHeight > 1)
                    {
                        jumpHeight -= JUMP_SPEED;
                    }

                    puppetPosition = new Point((int)(Noise.Generate( 2423423.0f, frame / 30.0f) *
                                                     Math.Abs(Noise.Generate( 24320.0f, frame / 75.0f) *
                                                      Noise.Generate( 52434.0f, frame / 125.0f) *
                                                      Noise.Generate( 23442.0f, frame / 250.0f)) * WALK_SPEED) + 
                                                      puppetPosition.X,
                                                (int)(Noise.Generate( 4243423.0f, frame / 30.0f) *
                                                     Math.Abs(Noise.Generate( 32421.0f, frame / 75.0f) *
                                                      Noise.Generate( 76570.0f, frame / 125.0f) *
                                                      Noise.Generate(57656.0f, frame / 250.0f)) * WALK_SPEED) +
                                                      puppetPosition.Y);

                    if (puppetPosition.X < 0 || puppetPosition.X > canvas.Width)
                    {
                        puppetPosition.X = (int)(canvas.Width / 2 + (Noise.Generate(frame, 29, 65)*(canvas.Width / 2)));
                    }
                    if (puppetPosition.Y < 0 || puppetPosition.Y > canvas.Height)
                    {
                        puppetPosition.Y = (int)(canvas.Height / 2 + (Noise.Generate(frame, 75, 42) * (canvas.Height / 2)));
                    }

                    critterNursery.GetCritterFrame(canvas,
                                                   tempCritter,
                                                   motion["walking"],
                                                   frame);
                    if (MIRRORED == true)
                    {
                        critterNursery.GetCritterFrame(canvas,
                                                        tempReflectionCritter,
                                                       motion["walking"],
                                                      frame);
                    }

                    break;
                case (1):
                    jumpHeight += JUMP_SPEED;

                    puppetPosition = new Point(puppetPosition.X,
                            (int)(Noise.Generate(878758.0f, frame / 300.0f) *
                                 Noise.Generate(523231.0f, frame / 750.0f) *
                                  Noise.Generate(11534530.0f, frame / 1250.0f) *
                                  Noise.Generate(5657.0f, frame / 2500.0f) * 10.0f +
                                  puppetPosition.Y));

                    if (puppetPosition.X < 0 || puppetPosition.X > canvas.Width)
                    {
                        puppetPosition.X = (int)(canvas.Width  + (Noise.Generate(frame, 29, 65)*(canvas.Width / 2)));
                        puppetPosition.Y = (int)(canvas.Height + (Noise.Generate(frame, 75, 42) * (canvas.Height / 2)));
                    }
                    if (puppetPosition.Y < 0 || puppetPosition.Y > canvas.Height)
                    {
                        puppetPosition.X = (int)(canvas.Width + (Noise.Generate(frame, 29, 65) * (canvas.Width / 2)));
                        puppetPosition.Y = (int)(canvas.Height + (Noise.Generate(frame, 75, 42) * (canvas.Height / 2)));
                    }

                    critterNursery.GetCritterFrame(canvas,
                                                   tempCritter,
                                                   motion["jumping"],
                                                   frame);
                    if (MIRRORED == true)
                    {
                        critterNursery.GetCritterFrame(canvas,
                                                      tempReflectionCritter,
                                                     motion["jumping"],
                                                    frame);
                    }
                    break;
                default:
                    if (jumpHeight > 1)
                    {
                        jumpHeight -= JUMP_SPEED;
                    }

                    critterNursery.GetCritterFrame(canvas,
                               tempCritter,
                               motion["waving"],
                               frame);

                    if (MIRRORED == true)
                    {
                         critterNursery.GetCritterFrame(canvas,
                                   tempReflectionCritter,
                                  motion["waving"],
                                 frame);
                    }
                        break;
            }
        }
    }
}
